#if defined(_MSC_VER)
#pragma once
#endif

#ifndef LM_CORE_NORMAL_H
#define LM_CORE_NORMAL_H

#include "lm.h"

class Normal 
{
public:
	Normal() : x(0), y(0), z(0) { }
	Normal(float val) : x(val), y(val), z(val) {	}
	Normal(float vx, float vy, float vz) : x(vx), y(vy), z(vz) { }
	explicit Normal(const Vector3 &v);
	explicit Normal(const Vector4 &v);
	explicit Normal(const Point3 &p);
#ifndef NDEBUG
	Normal(const Normal &n) : x(n.x), y(n.y), z(n.z) { }
	Normal& operator=(const Normal &n) {
		x = n.x; y = n.y; z = n.z;
		return *this;
	}
#endif
	Normal operator-() const {
		return Normal(-x, -y, -z);
	}
	Point3 operator+(const Point3 &p) const;
	Normal operator+(const Normal &n) const {
		return Normal(x+n.x, y+n.y, z+n.z);
	}
	Normal &operator+=(const Normal &n) {
		x += n.x; y += n.y; z += n.z;
		return *this;
	}
	Normal operator-(const Normal &n) const {
		return Normal(x-n.x, y-n.y, z-n.z);
	}
	Normal &operator-=(const Normal &n) {
		x -= n.x; y -= n.y; z -= n.z;
		return *this;
	}
	Normal operator*(float f) const {
		return Normal(x*f, y*f, z*f);
	}
	Normal &operator*=(float f) {
		x *= f; y *= f; z *= f;
		return *this;
	}
	Normal operator/(float f) const {
		if(f < EPSILON(12))
			Warning("Division by 0, in file %s, line %d\n", __FILE__, __LINE__);
		float invf = (float)1 / f;
		return Normal(x*invf, y*invf, z*invf);
	}
	Normal &operator/=(float f) {
		if(f < EPSILON(12))
			Warning("Division by 0, in file %s, line %d\n", __FILE__, __LINE__);
		float invf = (float)1 / f;
		x *= invf; y *= invf; z *= invf;
		return *this;
	}
	bool operator==(const Normal &n) const {
		return x == n.x && y == n.y && z == n.z;
	}
	bool operator!=(const Normal &n) const {
		return !(*this == n);
	}
	float LengthSqured() const {
		return x*x + y*y + z*z;
	}
	float Length() const {
		return sqrtf(x*x + y*y + z*z);
	}
	bool IsZero() const {
		return x == 0 && y == 0 && z == 0;
	}
	float operator[](int i) const {
		Assert(i >= 0 && i <= 2);
		return (&x)[i];
	}
	float &operator[](int i) {
		Assert(i >= 0 && i <= 2);
		return (&x)[i];
	}
	bool HasNaNs() const {
		return isnan(x) || isnan(y) || isnan(z);
	}
	std::string ToString() const {
		std::ostringstream oss;
		oss << "Normal " << "[" << x << ", " << y << ", "<< z << "]";
		return oss.str();
	}
	float x, y, z;
};

#endif	// LM_CORE_NORMAL_H
